import java.util.*;

class Ball {
    private int x = 3;
    private int y = 3;
    private int x_inc = 1;
    private int y_inc = 1;
    private int color = 35;   //ARGB 32
    private int ch = '@';
    static private int row = 60;
    static private int col = 15;
    static private int inc[] = {1, -1};
    static private int chs[] = {'A', 'B', '@', 'T', '%', '&', '*', 'G'};

    Ball(int ... msg) {
        if (msg.length == 0) {
            x = Kyo.rand(1, row - 2);
            y = Kyo.rand(1, col - 2);
            x_inc = inc[Kyo.rand(0, 1)];
            y_inc = inc[Kyo.rand(0, 1)];
            color = Kyo.rand(30, 36);
            ch = chs[Kyo.rand(0, chs.length - 1)];
        } else {
            set(msg);
        }
    }

    static void setInit(int row, int col) {
        Ball.row = row;
        Ball.col = col;
    }

    Ball set(int ... msg) {
        if (msg.length > 0)
            x = msg[0];
        if (msg.length > 1)
            y = msg[1];
        if (msg.length > 2)
            x_inc = msg[2];
        if (msg.length > 3)
            y_inc = msg[3];
        if (msg.length > 4)
            color = msg[4];
        if (msg.length > 5)
            ch = msg[5];
        return this;
    }

    Ball set(String name, int val) {
        switch (name) {
            case "x":
                x = val;
                break;
            case "y":
                y = val;
                break;
            case "x_inc":
                x_inc = val;
                break;
            case "y_inc":
                y_inc = val;
                break;
            case "color":
                color = val;
                break;
            case "ch":
                ch = val;
                break;
            default:
                break;
        }
        return this;
    }

    Ball setX(int val) {
        return set("x", val);
    }

    Ball setX() {
        return set("x", Kyo.rand(1, row - 2));
    }

    Ball setY(int val) {
        return set("y", val);
    }

    Ball setY() {
        return set("y", Kyo.rand(1, col - 2));
    }

    Ball setXInc(int val) {
        return set("x_inc", val);
    }

    Ball setYInc(int val) {
        return set("y_inc", val);
    }

    Ball setColor(int val) {
        return set("color", val);
    }

    Ball setColor() {
        return set("color", Kyo.rand(30, 36));
    }

    Ball setCh(int val) {
        return set("ch", val);
    }

    int get(String name) {
        switch (name) {
            case "x":
                return x;
            case "y":
                return y;
            case "x_inc":
                return x_inc;
            case "y_inc":
                return y_inc;
            case "color":
                return color;
            case "ch":
                return ch;
        }
        return 0;
    }

    boolean checkX() {
        return (x == row - 1 || x == 0) ? true : false;
    }

    boolean checkY() {
        return (y == col - 1 || y == 0) ? true : false;
    }

    Ball show() {
        System.out.printf("x = %d, y = %d, x_inc = %d, y_inc = %d,"
                          + "color = %d, ch = %d\n", x, y, x_inc, y_inc,
                                                     color, ch);
        return this;
    }
}

public class BallPro {
    static public final int ROW = 15;
    static public final int COL = 60;
    private char bg[][] = new char[ROW][COL];
    private Timer t = new Timer();
    private Ball ball[] = null;

    static private BallPro pro = null;

    static public BallPro getInstance(int num) {
        if (pro == null)
            pro = new BallPro(num);

        return pro;
    }

    static public void p(String format, Object ... obj) {
        System.out.printf(format, obj);
    }

    void init(char ch) {
        int i, j;
        for (i = 0; i < ROW; ++i) {
            for (j = 0; j < COL; ++j) {
                if (j == 0 || i == 0 || i == ROW - 1 || j == COL - 1)
                    bg[i][j] = ch;
                else
                    bg[i][j] = ' ';
            }
        }
    }

    void draw() {
        for (int i = 0; i < ball.length; ++i) {
            bg[ball[i].get("x")][ball[i].get("y")] = (char)i;
        }
    }

    void move() {
        for (int i = 0; i < ball.length; ++i) {
            if (ball[i].checkX())
                ball[i].setXInc(ball[i].get("x_inc") * -1);

            if (ball[i].checkY())
                ball[i].setYInc(ball[i].get("y_inc") * -1);

            ball[i].setX(ball[i].get("x") + ball[i].get("x_inc"));
            ball[i].setY(ball[i].get("y") + ball[i].get("y_inc"));
        }
    }

    void showBall(int index) {
        if (index == '#' || index == ' ') {
            p("%c", index);
        } else {
            char ch = (char)ball[index].get("ch");
            Vt.out("".valueOf(ch), ball[index].get("color"));
        }
    }

    void show() {
        int i, j;

        Vt.gotoxy(1, 1);
        for (i = 0; i < ROW; ++i) {
            for (j = 0; j < COL; ++j) {
                showBall(bg[i][j]);
            }
            p("\n");
        }
    }

    private BallPro(int ballNum) {
        Vt.cmd(Vt.SCREEN).cmd(Vt.HIDE);
        Kyo.init();

        Ball.setInit(ROW, COL);
        ball = new Ball[ballNum];

        for (int i = 0; i < ballNum; ++i) {
            ball[i] = new Ball();
        }

        t.schedule(new TimerTask() {
            public void run() {
                init('#');
                draw();
                move();
                show();
            }
        }, 100, 100);
    }

    void ballExit() {
        t.cancel();
        Kyo.exit();
        Vt.cmd(Vt.SHOW).gotoxy(ROW + 1, 1);
    }

    void run() {
        int in;

        while (true) {
            in = Kyo.key();
            if (in == 27)
                break;
        }
        ballExit();
    }

    static public void main(String [] args) {
        BallPro.getInstance(100).run();
    }
}
